// scaup - Scala up!
// (c) 2008-2010, Normen Müller, normen.mueller@gmail.com  
// $Id: Lexer.scala 47 2010-01-12 20:22:35Z normen.mueller $
package scaup.parsing.dot

import scala.util.parsing.combinator._
import scala.util.parsing.combinator.lexical._
import scala.util.parsing.input.CharArrayReader.EofCh

/** 
 * @author <a href="mailto:normen.mueller@googlemail.com">Normen M&#xFC;ller</a>
 * @version 0.0.3 */
private [dot] class Lexer extends StdLexical with RegexParsers {
  override type Elem = Char
  
  override def token =
    ( string                                 ^^ StringLit      
    | number ~ letter                        ^^ {case n ~ l => ErrorToken("Invalid number format: " + n + l)}
    | '-' ~ whitespace ~ number ~ letter     ^^ {case ws ~ num ~ l => ErrorToken("Invalid number format: -" + num + l)}
    | '-' ~ whitespace ~ number              ^^ {case ws ~ num => NumericLit("-" + num)}
    | number                                 ^^ NumericLit
    | EofCh                                 ^^^ EOF     
    | delim
    | '\"' ~> failure("Unterminated string")
    | id                                     ^^ {case i if reserved contains i => Keyword(i) case i => Identifier(i)}
    | failure("Illegal character")
    )
  
  // A string is a collection of zero or more Unicode characters, wrapped in double quotes, using backslash escapes.
  lazy val string = '\"' ~> rep(charSeq | chrExcept('\"', '\n', EofCh)) <~ '\"' ^^ { _ mkString "" }
  
  lazy val number = """-?(\d+(\.\d*)?|\d*\.\d+)([eE][+-]?\d+)?[fFdD]?""".r
  
  lazy val id = rep(letter | digit | elem("underscore", _=='_')) ^^ { _ mkString "" }
  
  lazy val charSeq = 
    ( '\\' ~ '\"' ^^^ "\"" | '\\' ~ '\\' ^^^ "\\" | '\\' ~ '/'  ^^^ "/" | '\\' ~ 'b'  ^^^ "\b" | '\\' ~ 'f'  ^^^ "\f" 
    | '\\' ~ 'n'  ^^^ "\n" |'\\' ~ 'r'  ^^^ "\r" |'\\' ~ 't'  ^^^ "\t" |'\\' ~ 'u' ~> unicodeBlock
    )
  
  import scala.io.UTF8Codec.{encode}
  lazy val unicodeBlock = hexDigit ~ hexDigit ~ hexDigit ~ hexDigit ^^ {
    case a ~ b ~ c ~ d => new String(encode(Integer.parseInt(List(a, b, c, d) mkString "", 16)))
  }
  
  val hexDigits = Set[Char]() ++ "0123456789abcdefABCDEF".toArray
  
  def hexDigit = elem("hex digit", hexDigits.contains(_))
}
